package com.quxing.amazon.security;

import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.quxing.amazon.common.BaseException;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpHeaders;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.concurrent.TimeUnit;

import static com.quxing.amazon.common.StatusConst.ACCOUNT_LOGIN_OTHER;
import static com.quxing.amazon.common.StatusConst.TOKEN_EXPIRED;

/**
 * @author zhb
 * @description
 * @date 2020/11/27 16:30
 */
@Configuration
@RequiredArgsConstructor
@Slf4j
public class JwtUtil {

    private static final String REDIS_JWT_KEY_PREFIX = "amazon:security:jwt:";
    private static final String SIGN = "quxing";
    private static final int TTL = 3;
    private static final TimeUnit TIME_UNIT = TimeUnit.DAYS;
    private static final DateField DATE_FIELD = DateField.DAY_OF_YEAR;
    private static final String BEARER = "Bearer ";
    private static final String USER_INFO_FILED = "userInfo";

    private final RedisTemplate<String, Object> redisTemplate;


    public static JwtUser getLoginUser() {
        return (JwtUser) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
    }

    public String createJwt(Authentication authentication) {

        JwtUser jwtUser = (JwtUser) authentication.getPrincipal();
        Date now = new Date();
        String jwt = Jwts.builder()
                .setId(jwtUser.getId().toString())
                .setSubject(jwtUser.getUsername())
                .setIssuedAt(now)
                .signWith(SignatureAlgorithm.HS256, SIGN)
                .claim(USER_INFO_FILED, jwtUser)
                .setExpiration(DateUtil.offset(now, DATE_FIELD, TTL))
                .compact();
        // 将生成的JWT保存至Redis
        redisTemplate.opsForValue().set(REDIS_JWT_KEY_PREFIX + jwtUser.getUsername(), jwt, TTL, TIME_UNIT);
        return jwt;
    }

    public Claims parseJwt(String jwt) {

        //解析token失败说明token非法或者过期，直接返回过期
        Claims claims = Jwts.parser()
                .setSigningKey(SIGN)
                .parseClaimsJws(jwt)
                .getBody();

        Object token = redisTemplate.opsForValue().get(getRedisKey(claims));
        //redis不存在token,说明过期
        if (token == null) {
            throw new BaseException(TOKEN_EXPIRED);
        }
        //存在token不一致说明账号被挤
        if (!StrUtil.equals(jwt, token.toString())) {
            throw new BaseException(ACCOUNT_LOGIN_OTHER);
        }

        return claims;
    }

    private String getRedisKey(Claims claims) {
        String username = claims.getSubject();
        return REDIS_JWT_KEY_PREFIX + username;
    }

    public void invalidateJwtToken(HttpServletRequest request) {
        String jwt = getJwtFromRequest(request);
        Claims claims = parseJwt(jwt);
        String username = claims.getSubject();
        invalidateJwtToken(username);
    }

    @CacheEvict(cacheNames = "jwtUser", key = "#username")
    public void invalidateJwtToken(String username) {
        redisTemplate.delete(REDIS_JWT_KEY_PREFIX + username);
    }


    public String getJwtFromRequest(HttpServletRequest request) {
        String bearerToken = request.getHeader(HttpHeaders.AUTHORIZATION);
        if (StrUtil.isNotBlank(bearerToken) && bearerToken.startsWith(BEARER)) {
            return bearerToken.substring(7);
        }
        return null;
    }

}
